#include <boost/shared_ptr.hpp>

#include "asserts.hpp"
#include "filesystem.hpp"
#include "string_utils.hpp"
#include "unit_type.hpp"
#include "wml_node.hpp"
#include "wml_parser.hpp"
#include "wml_utils.hpp"

namespace game {

namespace {
std::map<std::string, std::string> unit_file_paths;

typedef std::map<std::string, const_unit_type_ptr> UnitTypeMap;
UnitTypeMap cache;
}

const_unit_type_ptr unit_type::get(const std::string& id)
{
	UnitTypeMap::const_iterator i = cache.find(id);
	if(i != cache.end()) {
		return i->second;
	}

	if(unit_file_paths.empty()) {
		sys::get_unique_filenames_under_dir("data/units", &unit_file_paths);
	}

	std::map<std::string, std::string>::const_iterator path_itor = unit_file_paths.find(id + ".cfg");
	ASSERT_LOG(path_itor != unit_file_paths.end(), "Could not find file for object '" << id << "'");

	const_unit_type_ptr result = create(wml::parse_wml_from_file(path_itor->second));
	cache[id] = result;
	return result;
}

const_unit_type_ptr unit_type::create(wml::const_node_ptr node)
{
	if(node->name() == "commander") {
		return const_unit_type_ptr(new commander_unit_type(node));
	} else {
		return const_unit_type_ptr(new soldier_unit_type(node));
	}
}

unit_type::~unit_type()
{
}

int unit_type::attack() const { return attack_; }
int unit_type::defense() const { return defense_; }
int unit_type::aggression() const { return aggression_; }
int unit_type::movement() const { return movement_; }
int unit_type::attack_range() const { return attack_range_; }

unit_type::FIGHTING_STYLE unit_type::fighting_style() const { return fighting_style_; }

const unit_animation& unit_type::attack_animation() const
{
	return attack_animation_ ? *attack_animation_ : standing_animation();
}

const unit_animation& unit_type::defend_animation() const
{
	return defend_animation_ ? *defend_animation_ : standing_animation();
}

const unit_animation& unit_type::death_animation() const
{
	return death_animation_ ? *death_animation_ : standing_animation();
}

const unit_animation* unit_type::missile_animation() const
{
	return missile_animation_.get();
}

unit_type::unit_type(wml::const_node_ptr node)
  : description_(node->attr("description")),
    attack_(wml::get_int(node, "attack")),
    defense_(wml::get_int(node, "defense")),
    aggression_(wml::get_int(node, "aggression")),
    movement_(wml::get_int(node, "movement")),
	attack_range_(wml::get_int(node, "attack_range", 1)),
	fighting_style_(unit_type::FIGHTING_STYLE_MELEE),
    standing_animation_(node->get_child("stand")),
	portrait_(node->attr("portrait")),
	categories_(0)
{
	const std::string& fighting_style = node->attr("fighting_style");
	if(fighting_style == "archer") {
		fighting_style_ = FIGHTING_STYLE_ARCHER;
	}

	if(node->get_child("attack")) {
		attack_animation_.reset(new unit_animation(node->get_child("attack")));
	}

	if(node->get_child("defend")) {
		defend_animation_.reset(new unit_animation(node->get_child("defend")));
	}

	if(node->get_child("death")) {
		death_animation_.reset(new unit_animation(node->get_child("death")));
	}

	if(node->get_child("missile")) {
		missile_animation_.reset(new unit_animation(node->get_child("missile")));
	}

	std::vector<std::string> categories = util::split(node->attr("category"));
	foreach(const std::string& cat, categories) {
		categories_ = categories_|get_unit_category(cat);
	}

	FOREACH_WML_CHILD(bonus_node, node, "bonus") {
		modifications_.push_back(unit_modification::create(bonus_node));
	}
}

soldier_unit_type::soldier_unit_type(wml::const_node_ptr node)
  : unit_type(node), cost_(wml::get_int(node, "cost"))
{
}

commander_unit_type::commander_unit_type(wml::const_node_ptr node)
  : unit_type(node),
    num_commands_(wml::get_int(node, "commands")),
    command_radius_(wml::get_int(node, "command_radius")),
    attack_bonus_(wml::get_int(node, "attack_bonus")),
    defense_bonus_(wml::get_int(node, "defense_bonus"))
{
	if(node->has_attr("recruits")) {
		std::vector<std::string> recruits = util::split(node->attr("recruits"));
		foreach(const std::string& type, recruits) {
			const_unit_type_ptr type_ptr = get(type);
			const_soldier_unit_type_ptr soldier = boost::dynamic_pointer_cast<const soldier_unit_type>(type_ptr);
			if(soldier) {
				recruits_.push_back(soldier);
			}
		}
	}

	FOREACH_WML_CHILD(level_node, node, "level") {
		levels_.push_back(level_info(level_node));
	}
}

int commander_unit_type::num_commands() const { return num_commands_; }
int commander_unit_type::command_radius() const { return command_radius_; }
int commander_unit_type::attack_bonus() const { return attack_bonus_; }
int commander_unit_type::defense_bonus() const { return defense_bonus_; }

const std::vector<const_soldier_unit_type_ptr>& commander_unit_type::recruits() const
{
	return recruits_;
}

commander_unit_type::level_info::level_info()
  : experience_required(0), attack(0), defense(0),
    attack_bonus(0), defense_bonus(0), command_radius(0)
{
}

commander_unit_type::level_info::level_info(wml::const_node_ptr node)
  : experience_required(wml::get_int(node, "experience")),
    attack(wml::get_int(node, "attack")),
    defense(wml::get_int(node, "defense")),
    attack_bonus(wml::get_int(node, "attack_bonus")),
    defense_bonus(wml::get_int(node, "defense_bonus")),
    command_radius(wml::get_int(node, "command_radius"))
{
}

}
